/*! \file HXAlive.h
    \brief Head X semi-autonomous behaviour routine.

	Head X | Flinders University | Martin Luerssen & Trent Lewis | 2009+
*/

#pragma once

class HXAliveUnit;
class HXAliveCommand;
class HXAliveMotion;
class HXAliveProcessor;

#include "HXMatrix.h"
//#include "HXPlayer.h"
#include "HXConfig.h"
#include "HXDisplay.h"
#include "HXUtil.h"

typedef unsigned __int32 HXAliveType;
const HXAliveType HXAT_NONE = 0x00000000;
const HXAliveType HXAT_ALL = 0xFFFFFFFF;
const HXAliveType HXAT_IDS = 0x00000FFF;
const HXAliveType HXATF_ACTIVE = 0x00001000;
const HXAliveType HXATF_IDLE = 0x00002000;
const HXAliveType HXATF_COMMAND = 0x00004000;
const HXAliveType HXATF_ASYNC = 0x00008000;
const HXAliveType HXATF_MOTION = 0x00010000;
const HXAliveType HXATF_MORPH = 0x00100000;
const HXAliveType HXATF_ROTATION = 0x00200000;
const HXAliveType HXATF_TRANSLATION = 0x00400000;
const HXAliveType HXATF_SCALE = 0x00800000;
const HXAliveType HXATF_FG = 0x00020000;

//-----------------------------------------------------------------------------

//! Playback instruction.
class HXAliveUnit
{
public:
	HXAliveUnit(HXAliveType id = HXAT_NONE);
	virtual ~HXAliveUnit();
	void TriggeredStart(unsigned int index);
	bool IsTriggering();
	void Trig(unsigned int index, bool andBelow = false);
	virtual void Start() = 0;
	virtual bool IsFinished() = 0;
	unsigned int GetId() { return m_id; }

protected:
	HXAliveType m_id;
	unsigned int m_triggerIndex;
	bool m_triggerUse;
};

//! Instruction to execute a particular command.
class HXAliveCommand : public HXAliveUnit
{
public:
	HXAliveCommand(const wstring& command, bool async = true);
	void Start();
	bool IsFinished();

protected:
	wstring m_command;
	bool m_bExecuted;
};

//! Instruction to change something smoothly.
class HXAliveChange : public HXAliveUnit
{
public:
	HXAliveChange(HXAliveType id = HXAT_NONE);
	void Set(float duration, float delay = 0.0f, HXBlendMode mode = 0);
	void Extend(float duration, float delay = 0.0f);
	virtual void Start();
	void Start(float duration, float delay = 0.0f, HXBlendMode mode = 0);
	void SkipDelay();
	bool IsFinished();
	float GetElapsedTime();
protected:
	float GetChange(float& max, float& progress);
	float Transform(float position);

	float m_duration, m_delay;
	HXBlendMode m_transformMode;
	HXStopWatch m_timer;
};

//! Instruction to perform a particular motion.
class HXAliveMotion : public HXAliveChange
{
public:
	HXAliveMotion(HXAliveType id = HXAT_NONE);
	void SetMorph(const wstring& morphTarget, float morph_max);
	void SetRotation(float x_max, float y_max, float z_max);
	void SetTranslation(float x_max, float y_max, float z_max);
	void SetScale(float x_max, float y_max, float z_max);
	void SetGroup(const wstring& groupName);

	// shortcuts...
	wstring& GetMorphTarget() { return m_morphTarget; }
	float GetMorphChange() { return GetChange(m_morph_max, m_morph_progress); }
	float GetRotXChange() { return GetChange(m_rot_max[0], m_rot_progress[0]); }
	float GetRotYChange() { return GetChange(m_rot_max[1], m_rot_progress[1]); }
	float GetRotZChange() { return GetChange(m_rot_max[2], m_rot_progress[2]); }
	float GetTransXChange() { return GetChange(m_trans_max[0], m_trans_progress[0]); }
	float GetTransYChange() { return GetChange(m_trans_max[1], m_trans_progress[1]); }
	float GetTransZChange() { return GetChange(m_trans_max[2], m_trans_progress[2]); }
	float GetScaleXChange() { return GetChange(m_scale_max[0], m_scale_progress[0]); }
	float GetScaleYChange() { return GetChange(m_scale_max[1], m_scale_progress[1]); }
	float GetScaleZChange() { return GetChange(m_scale_max[2], m_scale_progress[2]); }
	float GetMorphProgress() { return m_morph_max-m_morph_progress; }
	float GetRotXProgress() { return m_rot_max[0]-m_rot_progress[0]; }
	float GetRotYProgress() { return m_rot_max[1]-m_rot_progress[1]; }
	float GetRotZProgress() { return m_rot_max[2]-m_rot_progress[2]; }
	float GetTransXProgress() { return m_trans_max[0]-m_trans_progress[0]; }
	float GetTransYProgress() { return m_trans_max[1]-m_trans_progress[1]; }
	float GetTransZProgress() { return m_trans_max[2]-m_trans_progress[2]; }
	float GetScaleXProgress() { return m_scale_max[0]-m_scale_progress[0]; }
	float GetScaleYProgress() { return m_scale_max[1]-m_scale_progress[1]; }
	float GetScaleZProgress() { return m_scale_max[2]-m_scale_progress[2]; }
	HXResourceGroup* GetGroup() { return m_pGroup; }

protected:

	wstring m_morphTarget;
	float m_morph_max;
	float m_morph_progress;
	HXVector3F m_rot_max;
	HXVector3F m_rot_progress;
	HXVector3F m_trans_max;
	HXVector3F m_trans_progress;
	HXVector3F m_scale_max;
	HXVector3F m_scale_progress;
	HXResourceGroup* m_pGroup;
};

//-----------------------------------------------------------------------------

//! Non-speech motion processor/generator.
class HXAliveProcessor
{
public:
	HXAliveProcessor();
	~HXAliveProcessor();

	void Run(HXDisplay* pDisplay);
	void Stop();

	void Add(HXAliveUnit* pCommand);
	size_t Count() { return m_aliveQueue.size(); }
	size_t Count(HXAliveType id);
	size_t Count(HXAliveType id, unsigned int subid);
	void Clear(unsigned int id = HXAT_ALL, bool clearTriggeredOnly = false);
	void SlowClear(unsigned int id = HXAT_ALL);
	void Callback();
	bool& Idle() { return m_bIdleBehaviour; }
	void Trig(unsigned int index, bool andBelow, unsigned int id = HXAT_ALL);
	HXDisplay* GetDisplay() { return m_pDisplay; }

protected:
	void IdleGenerate();

	list<HXAliveUnit*> m_aliveQueue;

	HXDisplay* m_pDisplay;
	bool m_bIdleBehaviour;
	HXStopWatch m_timer;

	CRITICAL_SECTION	m_criticalSection_Alive;
};

//=============================================================================

extern HXAliveProcessor g_alive;
